"""
Basics of python
Code written for Python 3.3.2
"""

def main():
    """
    # Print statement
    print("Hello World!!")
    # Concatenating strings using print function
    word = "Sami"
    print("hello", word)
    print("age = ", 32)
    # A print function inserts a \n character at the end of the line automatically and we can
    # see that if we hover over the print statement. To avoid using \n, use the 'end' property.
    print("This is line 1 - ", end='')
    print("This line is supposed to be in the same line")
    # Using the 'sep' property of the print function
    basket = ['Apple', 'Oranges', 'Banana']
    print(*basket, sep=", ", end=".\n")
    # The one last addition to the print function made by the Python team was the file
    # keyword argument. The 'file' property defaults to sys.stdout that is your terminal.
    # But it can be changed to any object that supports any write(string) method.
    # Lets try printing the fruit basket to a file than to the terminal as default
    fruits = open("fruitsfile.txt", "w+")
    basket = ('Apple', 'Oranges', 'Banana')
    print (*basket, sep=", ", end=".xxxx\n", file=fruits)
    """
    """
    # Taking input from the user and using type conversion
    x = int(input("Enter First Number: "))
    y = int(input("Enter Second Number: "))
    # Concatenation
    z = x + y
    print("The sum of the two numbers is: " + str(z))
    """
    """
    # Using modules (libraries)
    import math
    print(str(math.floor(3.9)))
    print(str(math.ceil(3.9)))
    print(str(math.sqrt(25)))
    """
    """
    # Another way to use functions
    myFunction = math.sqrt
    print("The square root of 100 is: " + str(myFunction(100)))
    """
    """
    # Using variables in between strings - Tutorial 17
    var = "Sami"
    print("My name is %s as you already know" % var)
    var = ("Sami", "Biryani")
    print("Hi %s, do you like %s?" % var)
    """
    """
    # Finding the index of words in a string
    var = "Hello, I think you are doing great"
    print(var.find("I"))
    """
    """
    # Sequences and Lists - Tutorial 9
    # List
    family = ['mom', 'dad', 'bro', 'sis', 'dog']
    print(family[0])
    print(family[-1])
    """
    """
    # Populating a list with sequentially numbered elements.
    # Sometimes we need to have sequentially numbered elements.
    # For that we can use lists to generate them.
    list = ["hey"+str(i) for i in range(10)]
    print(list)
    """
    """
    # Strings can also be indexed to pick out certain values
    # Equivalent to say that strings can also be used like a list
    print("Abdus Sami"[3])
    """
    """
    # Slicing - Tutorial 10
    # Slicing is used to extract out certain pieces of a sequence/list
    example = [0,1,2,3,4,5,6,7,8,9]
    print(example[4:8])
    print(example[4:]) # For including the last value too
    print(example[0:9:2]) # Using steps to get every second number
    print(example[0::2]) # Using steps to get every second number
    print(example[::-2]) # Count the list backward
    """
    """
    # Concatenating sequences/lists) - Tutorial 11
    seq1 = [1, 2, 3]
    seq2 = [4, 5, 6]
    seq3 = seq1 + seq2
    print(seq3)
    """
    """
    # Multiply items to form a list
    print([21] * 10)
    """
    """
    # Multiply string a number of times
    print("absami " * 10)
    """
    """
    # Check if a character is in a string
    myString = "biryani"
    print('b' in myString)
    print('mom' in family)
    """
    """
    # Some List functions - Tutorial 12
    numbers = [4, 1, 5, 2, 5, 6, 4, 3, 5]
    print(len(numbers))
    print(max(numbers))
    print(min(numbers))
    print(list("absami"))
    numbers[0] = 15 # Change the value of a list item
    """
    """
    # Delete an item from a list - note that 2 is deleted in the list
    del(numbers[3])
    print(numbers)
    """
    """
    # Some list functions
    x = ['I', 'do', 'not', 'know', 'python', 'or', 'know', 'any']

    # Counts the occurence of an element in a list
    print(x.count('know')) 

    # Append a single element to a list
    x.append('anaconda') 
    print(x)

    # With multiple values we cannot use 'append', we use 'extend'
    one = [1, 2, 3]
    two = [4, 5, 6]
    one.extend(two) 
    print(one)
    """
    """
    # Some more list functions - Tutorial 15
    say = ['hey', 'now', 'brown', 'cow']
    print(say.index('brown'))
    say.insert(2, 'big')
    print(say)
    # To remove elements from a list, we can use 'pop()'
    say.pop(1)
    print(say)
    # Remove the first occurence of an element, use 'remove()'
    say.remove('brown')
    print(say)
    # reverse()
    say.reverse()
    print(say)
    """
    """
    # sort() in ascending order - Tutorial 16
    numbers = [15, 5, 3, 8, 10, 2]
    numbers.sort()
    print(numbers)
    # Strings can also be sorted using sorted()
    print(sorted("AbdusSami"))
    # Tuple - is just like list but it cannot be modified
    # Used only to call our values back
    tuplex = (22, 33, 47, 10, 15)
    print(tuplex[2])
    """
    """
    # Some string functions - Tutorial 18
    str1 = "heY nOw"
    print(str1.lower()) # Lower case
    print(str1.upper()) # Upper case
    truth = "I love my Ammi" # replace a word in a string
    truth = truth.replace("Ammi", "Abbu")
    print(truth)
    # Inserting a character (or a word) after every word in a list
    list1 = ["This", "is", "a", "list", "so", "what"]
    inserter = "OK"
    newList = inserter.join(list1)
    print(newList)
    # To remove white space from the right or left of a string
    str = "   String   "
    print(str.strip())
    # To remove characters from the right or left of a string
    str = "aaaString__"
    print(str.rstrip("_"))
    print(str.lstrip("a"))
    """
    """
    # dictionary - Tutorial 19
    # In python, dictionary is a structure having key-value pairs
    ages = {"mrA": 32, "mrB": 38, "mrC": 33}
    print("mrB is aged %s" % ages["mrB"])
    # some dictionary functions
    # clear() removes all items from the dictionary
    ages.clear()
    print("After clearing the ages dictionary, these are the contents - ", ages)
    # copy() a dictionary to another new dictionary
    ages = {"mrA": 32, "mrB": 38, "mrC": 33}
    newAges = ages.copy()
    print("After copying the ages to the new dictionary we get:", newAges)
    # returns 'true' if a dictionary has a particular key value
    print("Key found for mrA in newAges: ", ("mrA") in newAges)
    print("Key found for mrX in newAges: ", ("mrX") in newAges)

    # Looping through a dictionary, first method
    # for key in d: will simply loop over the keys in the dictionary,
    # rather than the keys and values.
    ages = {"mrA": 32, "mrB": 38, "mrC": 33}
    for (item) in ages:
        print(item, ages[item])

    # Looping through a dictionary, second method (considered better)
    # To loop over both key and value you can use for key, value in d.items()
    for key, value in ages.items():
        print(key, value)

    # It's a good idea going forward to get into the habit of using format strings,
    # and proper key and value names as variables
    for person, age in ages.items():
        print("{0} corresponds to {1}".format(person, age))

    # Get the list of keys and values of a dictionary.
    # 1. If we use the dictItem.keys() or the dictItem.values() directly, even though
    # we will get a result, we won't be able to index it because it is not a list.
    # 2. Remember that the returned values will most probably be not in order.
    d = {'x': 1, 'y': 2, 'z': 3}
    keys = list(d.keys())
    values = list(d.values())
    print("List of keys: ", keys)
    print("List of values: ", values)

    # Dictionaries: Adding a single item
    dict1 = {"key1": "value1", "key2": "value2", "key3": "value3"}
    dict1["key4"] = "value4"
    for k, v in dict1.items():
        print(k, v)

    # Dictionaries: Adding multiple items
    dict1.update({"key5": "value5", "key6": "value6", "key7": "value7"})
    for k, v in dict1.items():
        print(k, v)
    """
    
    """
    # 'if', 'elif' and 'else' statements - Tutorial 20, Tutorial 21
    fish = "tuna"
    if fish=="tuna":
        print("This is a fish!")
    elif fish=="salmon":
        print("This is also a fish!")
    elif fish=="pomphret":
        print("Also a fish!")
    else:
        print("I don't know what this is!")
    """
    """
    # Nested 'if' statements - # Tutorial 22
    typeOfCritter = "animal"
    species = "cat"
    if typeOfCritter == "animal":
        print("It is an animal alright!")
        if species == "cat":
            print("It is a cat!")
    else:
        print("Don't know what that is!")
    """
    """
    # Single line if statement
    x = 3
    y = 4
    str = "x is less than y" if(x < y) else "x is greater than or equal to y"
    print(str)
    """
    """
    # Other comparison operators like <, >, <=, >=, !=, 'is', 'in' possible - Tutorial 23
    # The 'in' operator
    pizza = "pizzahut"
    print("Is there 'p' in 'pizza' - ", "p" in pizza)
    """
    """
    # Another type of if statement (called a conditional statement) (it's just like the ternary operator in C)
    # Conditional statement - a if(Condition) else b
    x = 10
    y = 20
    result = "x is less than y" if(x <  y) else "x is greater than or equal to y"
    print(result)
    """
    """
    # 'and' and 'or' keywords - Tutorial 24
    number = 6
    if number > 5 and number < 10: # 'and' keyword
        print("Number is between 5 and 10")
    if number > 5 or number < 3:
        print("Either the number is greater than 5 or it is less than 3")
    """
    """
    # Loops - Tutorial 25
    # While loop
    var = 1
    while var < 10:
        print(var)
        var += 1
    """
    """
    # For loop - to loop through collections (lists)
    groceryList = ['bread', 'milk', 'chicken', 'beef']
    for (food) in groceryList:
        print("I want %s" % food)
    """
    """
    # Simple for loop as we generally use
    print("An example of for loop, how range is used and how format() is used")
    for i in range(1, 11):
        print("{:>10} {:>10} {:>10}".format(i, i**2, i**3))
    """
    """	
    # Using break (breaks the loop) and continue (go back to the start of the loop and continue from there)
    for x in range(5, 10):
            if(x==7): break
            print(x)

    for x in range(5, 10):
            if(x%2 == 0):
                    continue
    print(x)
    """
    """
    # Using the enumerate() function to get the index of items in a list
    days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
    for i, d in enumerate(days):
            print(i, " ", d)
    """
    """
    # Infinite 'while' loop with 'break' - Tutorial 26
    var = 1
    while 1:
        print("A")
        var += 1
        if var == 10:
            break
    """
    """
    # Special single line for loop to generate (multi-dimensional) lists.
    rowCount = 3
    columnCount = 4
    tableData = [["Hey now" for i in range(columnCount)] for j in range(rowCount)]
    print(tableData)
    """
    """
    # Fibonacci using while loop
    # Also notice how we have used assignment operator here.
    print("Fibonacci series using while loop")
    a, b = 0, 1
    while (b < 50):
        print(b)
        a, b = b, a + b
    """
    #"""
    # Generators
    # Iterables: Everything you can use "for... in..." on is an iterable: lists, strings, files...
    # These iterables are handy because you can read them as much as you wish, but you "store all
    # the values in memory" and it's not always what you want when you have a lot of values.
    # Generators are iterators, but you can only iterate over them once. It's because they do not
    # store all the values in memory, they generate the values on the fly.
    # A generator is a function that produces a sequence of results instead of a single value.
    # Instead of returning one value, you generate a series of values (using 'yield' statement)
    # Calling a generator function creates a 'generator object'. However it does not start
    # running the generator function. Also note that no output is produced in the function body.
    # The function only executes on next().
    # 'yield' produces a value but suspends the function. The function then resumes on the next
    # call to next() function.
    # A generator function is slightly different from an object that supports iteration.
    # A generator is a one time operation. You can iterate over the generated data once, but if
    # you want to do it again, you have to call the generator function again.
    # This is different than a list (which you can iterate as many times as you want).
    # Generators (in conjuntion with 'yield' keyword) are useful especially when you know that your
    # function will return a "huge" set of values that you know you'll need to read only once.
    # Generators can be used in two ways: 1. As a generator expression, 2. As a generator function.
    # In both cases we get an object that generates values (which are typically consumed in a for loop)
    # An Example of Generator expressions
    a = [1, 2, 3, 4]
    b = (2*x for x in a)    # This format is called a generator expression
    print("Running generator first time")
    for i in b:
        print(i)
    print("Running generator first time")
    for i in b:
        print(i)

    # The 'yield' keyword (used when implementing a generator as a function)
    # It is used like a 'return' except that it returns a generator.
    # 'yield' produces a value and suspends the function. The function resumes on the next call
    # to next(). So when we need the next value, use the next() function.
    # Example of 'yield' and a generator function
    def countdown(n):
        print("Counting down from ", n)
        while n>0:
            yield(n)
            n = n - 1
    x = countdown(10)
    print("x is a :", x)
    print(next(x))
    print(next(x))
    print(next(x))
    print(next(x))

    # Generator function (another example: Calculating primes less than 100)
    # This is just a utility function that returns a boolean value if a number is prime or not.
    def isPrime(n):
        if n==1:
            return False
        for x in range(2, n):
            if n % x == 0:
                return False
        else:
            return True

    # This is the generator function
    def primes(n = 1):
        while(True):
            if isPrime(n):
                yield(n)
            n = n + 1

    # Printing the output
    for n in primes():
        if n > 100:
            break
        print(n)
    #"""
    """
    # User defined Functions - Tutorial 27
    # Note that here function is defined above its call
    def add(x, y):
        print("The sum is ", x+y)
    add(3, 4)
    """
    """
    # Functions with default values - Tutorial 28
    def name(first='Tom', last='Smith'):
        print("%s %s" % (first, last))
    name("abdus", "sami")
    name(first="Joe")
    name(last="black")
    """
    """
    # Functions with variable number of arguments
    def multi_add(*args):
            result = 0
            for x in args:
                    result = result + x
            return result
            
    print(multi_add(1, 3, 2, 4, 5))
    """
    """
    # Tuples and Dictionaries as parameters to function - Tutorial 30
    # *varName creates a tuple from the arguments
    # **varname create a dictionary from the arguments
    def profile(first, last, *ages, **food):
        print(first, last)
        print(ages)
        print(food)
    print("notice how the values are formed into a tuple and a dictionary")
    profile("abdus", "sami", 12, 12, 22, 48, 48, tikka=1, fish=3, drink=2)
    """
    """
    # Classes and Objects - Tutorial 32
    # Class definition
    class exampleClass:
        eyes="brown"
        age=30
        height=187
        def classMethod(self):
            print("This method worked")
    # Create class
    exampleClass
    # Create object
    exampleObject = exampleClass()
    print("Object eye color: ", exampleObject.eyes)
    print("Object age: ", exampleObject.age)
    print("Object height: ", exampleObject.height)
    exampleObject.classMethod()
    """
    """
    # Classes and self - Tutorial 33
    class className:
        def createName(self, name): # note that the first parameter will always be
            self.name = name # self. It is a placeholder for the object
            print("Name created - %s" % self.name)
        def displayName(self):
            return self.name
        def saying(self):
            print("Hello %s" % self.name)
    firstObj = className();
    firstObj.createName("Habiby")
    print(firstObj.displayName())
    firstObj.saying()
    """
    """
    # Subclasses and Superclasses - One class inherits the traits of another class
    # - Tutorial 34
    class parentClass:
        var1 = "I am variable1"
        var2 = "I am variable2"
    class childClass(parentClass): # Inherits the variables and methods of the first class
        # pass # Don't do anything
        # We can also overwrite the inherited variables and methods
        var2 = "This is the Overwritten variable 2"

    parentObj = parentClass()
    print("parentObj.var1 = ", parentObj.var1)
    childObj = childClass()
    print("childObj.var1 = ", childObj.var1)
    print("Overwritten - childObj.var2 = ", childObj.var2)
    """
    """
    # Subclasses with multiple parent classes - Tutorial 36
    class ParentClass1:
        var1 = "This is var1 from parent class 1"
    class ParentClass2:
        var2 = "This is var2 from parent class 2"
    class ChildClass(ParentClass1, ParentClass2):
        pass
    childObj = ChildClass()
    print("Calling var1 from ParentClass1 thru childObj: ", childObj.var1)
    print("Calling var2 from ParentClass2 thru childObj: ", childObj.var2)
    """
    """
    # Constructors - Tutorial 37
    class new:
        def __init__(self): # whenever an object is made, this function is called
            print("this is a constructor")
            print("print this too!")
    newObj = new()
    """
    """
    # Importing modules - Tutorial 38, 39
    # Note: you cannot import a module more than once in 2.7. You must restart the
    # shell using Ctrl+F6. Not a problem in 3.3
    import testModule
    testModule.add(2, 3)
    # Functions in a module - Tutorial 40
    import math
    dir(math) # View the functions in the module
    help(math) # Brief summary of each function in the module
    print("The math module: ", math.__doc__) # Brief summary about the module itself
    """
    """
    # Working with files - Tutorial 41
    # 'r'   open for reading (default). Read-only mode. The file pointer is placed at the beginning of the file.
    # 'r+'  Read-write mode. The file pointer will be at the beginning of the file.
    # 'w'   open for writing, truncating the file first. Write-only mode. Overwrites the file if the file exists. If the file
            does not exist, creates a new file for writing.
    # 'w+'  Read-write mode. Overwrites the existing file if the file exists. If the
            file does not exist, creates a new file for reading and writing.
    # 'x'   open for exclusive creation, failing if the file already exists
    # 'a'   open for writing, appending to the end of the file if it exists. Write-only mode. The file pointer is at the end of the file if the file
            exists. That is, the file is in the append mode. If the file does not exist,
            it creates a new file for writing.
    # 'a+'  Read and write mode. The file pointer is at the end of the file if the file
            exists. The file opens in the append mode. If the file does not exist, it
            creates a new file for reading and writing.
    # 'b'   binary mode
    # 't'   text mode (default)
    # '+'   open a disk file for updating (reading and writing)

    # Writing - Open a file with write permissions, write to it and then close it
    # Note that
    # fileObject = open("D:/My_Documents/python_files/theNewBoston/file.txt","w")
    fileObject = open("file.txt","w")
    fileObject.write("hey now brown cow")
    fileObject.close()
    """
    """
    # Reading - Open a file with read permissions, read from it and then close it
    # read() - reads the whole file, read(num) - reads the specified number of chars
    fileObject = open("fileToRead.txt","r")
    print(fileObject.read(3)) # Read the first 3 characters
    print(fileObject.read()) # Read the rest of the characters
    fileObject.close()
    """
    """
    # Read one single line upto the first break - Tutorial 42
    fileObject = open("fileToRead.txt","r")
    print(fileObject.readline())
    fileObject.close()
    """
    """
    # Read all the rest of the lines and form them into a list
    fileObject = open("fileToRead.txt","r")
    print(fileObject.readlines())
    fileObject.close()
    """
    """
    # We cannot use the read function because once it reaches at the end of the file
    # then it will not be reset again. To move the cursor on the position that we
    # want to read from and onwards is through using the 'seek()' function
    fileObject = open("fileToRead.txt","r")
    print("Output from the seek() function")
    print(fileObject.seek(10))
    print(fileObject.read())
    fileObject.close()
    """
    """
    # Get the location of the cursor in the file to be read, 'tell()' function
    fileObject = open("fileToRead.txt","r")
    print("first move the cursor to location 15")
    print(fileObject.seek(15))
    print("the cursor location is: ", fileObject.tell())
    print(fileObject.read())
    fileObject.close()
    """
    """
    # Read a certain number of characters from a file
    fileObject = open("fileToRead.txt", "r")
    print("Reading the first 30 characters from the file")
    print(fileObject.read(30))
    fileObject.close()
    """
    """
    # Another way of opening file is using the 'with as' statment
    # One Benefit - Python closes the file automatically at the end of the block
    # Another benefit - In case of error, the file is closed automatically
    with open("fileToRead.txt", "r") as fileObject:
        print("Inside the with-as block, reading the file...")
        print(fileObject.read())

    # Writing a line to a text file
    fileObject = open("file.txt","w")
    fileObject.write("you're fit, holy shit\ntakizo boraya\ntitiz ma favorite\n")
    print("some lines written to file.txt")
    fileObject.close()
    """
    """
    # Modify only certain lines in a file - Tutorial 43
    # First, read all the lines
    fileObject = open("file.txt","r")
    linesInTheFile = fileObject.readlines()
    print("All lines from file.txt are read into a list")
    fileObject.close()
    # Then modify the list item
    linesInTheFile[2] = "This line has been modified"
    # Then write the modified list back to the file
    fileObject = open("file.txt","w")
    fileObject.writelines(linesInTheFile)
    print("Written the modified lines to file.txt")
    fileObject.close()
    """
    """
    # Pickles - Tutorial 25 - themonkeylords
    # In python, pickling refers to putting (or dumping) different types of objects
    # e.g. lists into a file.
    # Note that the file must be opened in binary mode (i.e. wb or rb etc) to enable
    # pickling in python. The rest is the same
    import pickle
    listOfGFs = ["electronics", "vb6", "avr", "java", "now python"]
    # Dumping objects through pickling
    with open("pickle.txt", "wb") as fileObj  ect:
        print("dumping a listOfGFs into pickle.txt through pickling")
        pickle.dump(listOfGFs, fileObject)
    with open("pickle.txt", "rb") as fileObject:
        print("Loading back objects dumped through pickling")
        print(pickle.load(fileObject))
    """
    """
    # Get range of from 0 to the number of elements in a list
    list1 = ["a", "b", "c", "d", "e", "f"]
    print(range(len(list1)))
    """
    """
    # The python "zip()" function
    # zip(*iterables): Make an iterator that aggregates elements from each of the
    # iterables. In easy words,
    # The "zip" function returns tuples from combination of elements in a list
    # Make tuples from elements of two lists
    for t in zip(["a","b","c","d"], ["x","y"]):
        print(t)
    # Make tuples from elements of three lists
    for t in zip(["a","b","c","d"], ["x","y"], ["q","w","e"]):
        print(t)
    # Make tuple using a list and a range with numeric output
    for t in zip(["a","b","c","d"], [i for i in range(0,5)]):
        print(t)
    # Make tuple using a list and a range with character/string return type
    for t in zip(["a","b","c","d"], ["x" for i in range(0,5)]):
        print(t)
    # Strings can also be used like a list
    for t in zip('abcd', 'xy'):
        print(t)
    # Make tuple using a string and a range with character/string return type
    for t in zip('HeyNow', ['x' for i in range(0,5)]):
        print(t)
    # The two implementations below are equivalent for me. There may be a difference
    # in the two under the hood, but they are the same in output.
    # The latter is used in the enums section of this code.
    list = ["Two", "and", "two", "is", "four"]
    for t in zip(list, [i for i in range(len(list))]):
        print(t)
    for t in zip(list, range(len(list))):
        print(t)
    """
    """
    # How enums are emulated in PyQt/PySide
    # Python does not have support for enumerators so we have to use
    # some different tricks to emulate them.
    def enum(*enumerated):
        print(enumerated)
        print(range(len(enumerated)))
        # Create tuples from the enumerated list and a numeric value.
        print(zip(enumerated, range(len(enumerated))))
        # Form a dictionary using the tuple
        enums = dict(zip(enumerated, range(len(enumerated))))
        repr(enums)
        # Add a new item to the dictionary with "names" as key and list of the
        # passed "enumerated" arguments as value.
        # "names" is actually a list containing all the elements that were
        # passed to the enum function. Thus using the numeric values
        # (e.g. x = ENUM.ZERO) as index to names[x], give the name of the item.
        enums["names"] = enumerated
        repr(enums)
        print(enums)
    # We have the dictionary with the keys and values. We want to use the
    # keys as properties and values as property values in a new type that
    # we will create at runtime.
    # For that we will use the "type(name, bases, dict)" function.
    # The type function returns the type of an object. The return value is a "type object".  The isinstance() built-in 
    # function is recommended for testing the type of an object.
    # With three arguments, return a new type object. This is essentially a dynamic form of the class statement.
    # The name string is the class name and becomes the __name__ attribute; the bases tuple itemizes the base classes 
    # and becomes the __bases__ attribute; and the dict dictionary is the namespace containing definitions for class body 
    # and becomes the __dict__ attribute.
    # What actually is going on here is that we have the dictionary in "enums
    # variable", then we create a new type with the name "enum" and we pass the
    # dictionary to it. This will return us an object of type - enum which we
    # can use as an enumerated list.
        return type('enum', (), enums)
    # To see if the enum is working, first create one.
    ENUM = enum("ZERO", "ONE", "TWO")
    # Executing these statements will give us the numbers associated with ZERO,
    # ONE and TWO i.e. 0, 1 and 2 respectively.
    print(ENUM.ZERO)
    print(ENUM.ONE)
    print(ENUM.TWO)
    # Print the names using index into the list "names"
    print(ENUM.names[ENUM.ZERO])
    print(ENUM.names[ENUM.ONE])
    print(ENUM.names[ENUM.TWO])
    """
    """
    # Working with OS
    # Sometimes we need to get information on a file like if it exists, what is the path of
    # the file is, what directory the file is in etc.
    # Python provides path related utilities for this purpose.
    import os
    from os import path
    import datetime
    from datetime import date, time, timedelta
    import time
    
    # Get the name of the Operating system
    print("The name of the operating system is: ", os.name)

    # Check if a file exists and its type
    print("Item exists: ", path.exists("file.txt"))
    print("Item is a file: ", path.isfile("file.txt"))
    print("Item is a directory: ", path.isdir("file.txt"))

    # Working with file paths
    print("Item's path: ", path.realpath("file.txt"))
    print("Item's path and name: ", path.split(path.realpath("file.txt")))

    # Get the time of modification of the file
    # ctime() converts the time into readable form
    t = time.ctime(path.getmtime("file.txt"))
    print("This file was last modified on (ctime format): ", t)
    # We can also construct a datetime object from the modified time using fromtimestamp() function.
    print("Last modified (datetime format): ", datetime.datetime.fromtimestamp(path.getmtime("file.txt")))

    # Calculate how long ago the item was modified
    td = datetime.datetime.now() - datetime.datetime.fromtimestamp(path.getmtime("file.txt"))
    print("The file file.txt was modified: ", td, " ago")
    print("In seconds, the file file.txt was modified: ", td.total_seconds(), " seconds ago")
    """
    """
    # Working with shell utilities for manipulating files
    import os
    from os import path
    import shutil
    from shutil import make_archive
    from zipfile import ZipFile
	
	from os.path import dirname, realpath
	# Get the current working directory
	print(os.getcwd())
	# Get one directory above the current working directory
	print(os.path.dirname(os.getcwd()))
	
	# Alternately, get current working directory of the file
	print(os.path.dirname(realpath(__file__)))
	# One directory above the working directory
	print(os.path.dirname(os.path.dirname(realpath(__file__))))
	# Even one more directory above the current working directory
	print(os.path.dirname(os.path.dirname(os.path.dirname(realpath(__file__)))))

    # Make a duplicate of an existing file
    if path.exists("file.txt"):
        # Get the path to the file in the current directory in the src variable
        src = path.realpath("file.txt")

        # Separate the path from the file name
        head, tail = path.split(src)
        print("Path: ", head)
        print("File: ", tail)

        # Let's make a backup copy by appending .bak to the file name
        dst = src + ".bak"
        # Now use the shell to make the copy of the file
        shutil.copy(src, dst)
        # Copy over the permissions, modification time, or other meta data
        shutil.copystat(src, dst)
        # Rename the original file
        os.rename("file.txt", "newfile.txt")

        # Now put things into a zip archive
        # make_archive() creates an archive file that contains the contents of the whole directory
        root_dir, tail = path.split(src)
        shutil.make_archive("archive", "zip", root_dir)

        # Create a zip file and add two files to it. Python can also create tar files etc.
        with ZipFile("testzip.zip", "w") as newZip:
            newZip.write("newfile.txt")
            newZip.write("file.txt.bak")
    """
    
    print("done")
    input("Press any key to exit...")

if __name__ == "__main__":
    main()
